
#pragma once


#include <Arduino.h>

#include "arduino_timeout_counter.hpp"
#include "modpash_debug.hpp"

namespace modpash {

    using ArduinoSerialType = decltype(Serial);


    // enum class transmission_state {
    //     ok,
    //     timeout,
    //     terminated,
    //     unavailable,
    // };


    // struct transmission_result {
    //     size_t count;
    //     transmission_state state;
    // };

    namespace backend_error {

        constexpr int timeout = -1;
        // constexpr int unavailable = -2;

    }  // namespace backend_error


    class ArduinoSerialBackend {
       private:
        ArduinoSerialType &_serial;

        scheduler_basic::ArduinoTimeoutCounter _rx_timeout{0};
        scheduler_basic::ArduinoTimeoutCounter _tx_timeout{0};

       public:
        ArduinoSerialBackend(ArduinoSerialType &serial) :
            _serial(serial) {
        }

        void begin(uint32_t baud, uint8_t config) {
            _serial.begin(baud, config);
        }

        void end() {
            _serial.end();
        }

        int rx_available() const {
            return _serial.available();
        }

        int tx_available() const {
            return _serial.availableForWrite();
        }

        void set_rx_timeout(uint32_t t) {
            _rx_timeout.set(t);
        }

        uint32_t get_rx_timeout() const {
            return _rx_timeout.get();
        }

        void set_tx_timeout(uint32_t t) {
            _tx_timeout.set(t);
        }

        uint32_t get_tx_timeout() const {
            return _tx_timeout.get();
        }

        /**
         * @brief
         *
         * @param buf
         * @param count
         * @param terminator
         * @return int
         */
        int read(uint8_t *buf, size_t count) {
            size_t i = 0;
            for (; i < count; i++) {
                int r = get();
                if (r < 0) {
                    break;
                }

                buf[i] = static_cast<uint8_t>(r);
            }

            return i;
        }

        int write(uint8_t *buf, size_t count) {
            size_t i = 0;

            for (; i < count; i++) {
                int r = put(buf[i]);
                if (r < 0) {
                    break;
                }
            }

            return i;
        }


        int get() {
            // 如果不启用超时控制，则切换为无阻塞模式，立即返回
            _rx_timeout.start();
            while (rx_available() < 1) {
                if (_rx_timeout.expired()) {
                    return backend_error::timeout;
                }
            }

            return _serial.read();
        }

        int put(uint8_t d) {
            // 如果不启用超时控制，则切换为无阻塞模式，立即返回
            _tx_timeout.start();
            while (tx_available() < 1) {
                if (_tx_timeout.expired()) {
                    return backend_error::timeout;
                }
            }

            _serial.write(d);
            return 1;
        }
    };

}  // namespace modpash
